package cn.lg.soar.system.general.service.impl;

import cn.lg.soar.common.algorithm.RSAUtils;
import cn.lg.soar.common.algorithm.SM2Utils;
import cn.lg.soar.core.config.cache.template.ICacheTemplate;
import cn.lg.soar.mvc.util.RequestUtils;
import cn.lg.soar.mvc.util.ServletUtils;
import cn.lg.soar.system.general.service.IPublicService;
import com.wf.captcha.ArithmeticCaptcha;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.time.Duration;
import java.util.Objects;

/**
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@RefreshScope
public class PublicServiceImpl implements IPublicService {

    private final static String RSA_KEY = "rsa";
    private final static String SM2_KEY = "sm2";
    private final static String CAPTCHA_KEY = "cta";
    @Value("${lg.general.rsa.duration:120s}")
    private Duration rsaDuration;
    @Value("${lg.general.captcha.duration:120s}")
    private Duration duration;

    @Autowired
    private ICacheTemplate<String, Object> cacheTemplate;
    @Autowired
    private ICacheTemplate<String, byte[]> byteArrayCacheTemplate;

    @Override
    public byte[] getRSAPublicKey(String bizType) {
        String cKey = this.getKey(PublicServiceImpl.RSA_KEY, bizType);
        RSAUtils.Key key;
        try {
            key = RSAUtils.createKey();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        this.byteArrayCacheTemplate.put(
                cKey,
                key.getPrivateKey(),
                this.rsaDuration
        );
        return key.getPublicKey();
    }

    @Override
    public byte[] getRSAPrivateKey(String bizType) {
        String key = this.getKey(PublicServiceImpl.RSA_KEY, bizType);
        byte[] bytes = this.byteArrayCacheTemplate.get(key);
        this.byteArrayCacheTemplate.delete(key);
        if (bytes == null) {
            return new byte[0];
        }
        return bytes;
    }

    @Override
    public byte[] getSM2PublicKey(String bizType) {
        String cKey = this.getKey(PublicServiceImpl.SM2_KEY, bizType);
        SM2Utils.Key key;
        try {
            key = SM2Utils.createKey();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        this.byteArrayCacheTemplate.put(
                cKey,
                key.getPrivateKey(),
                this.rsaDuration
        );
        return key.getPublicKey();
    }

    @Override
    public byte[] getSM2PrivateKey(String bizType) {
        String key = this.getKey(PublicServiceImpl.SM2_KEY, bizType);
        byte[] bytes = this.byteArrayCacheTemplate.get(key);
        this.byteArrayCacheTemplate.delete(key);
        if (bytes == null) {
            return new byte[0];
        }
        return bytes;
    }

    @Override
    public String getCaptcha(String bizType, boolean toBase64) {

        String ck = this.getKey(CAPTCHA_KEY, bizType);
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(130, 48);
        this.cacheTemplate.put(ck, captcha.text(), this.duration);

        if (toBase64) {
            return captcha.toBase64();
        }

        HttpServletResponse response = ServletUtils.getResponse();
        try {
            captcha.out(response.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    @Override
    public boolean checkCaptcha(String bizType, String code) {
        String ck = this.getKey(CAPTCHA_KEY, bizType);
        Object o = this.cacheTemplate.get(ck);
        this.cacheTemplate.delete(ck);
        if (o == null) {
            return false;
        }
        return Objects.equals(code, o.toString());
    }

    private String getKey(String type, String bizType) {
        return MessageFormat.format("{0}:{1}:{2}", type, bizType, RequestUtils.clientId());
    }

}
